#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <windows.h>

typedef struct Rect{
	uint32_t oriX;
	uint32_t oriY;
	uint32_t endX;
	uint32_t endY;
}Rect_t;

typedef struct Page{
	union{
		uint32_t pWidth;	//page width;  unit:dot 300dpi; 内存申请时的宽度 8的倍数，用于计算缓冲区
		struct {
			uint32_t WRESERVED:3;
			uint32_t pWidth_B:29;	//page width 以字节为单位
		};
	};
	uint32_t pLength;	//page length; unit:dot 300dpi; 内存申请时的长度，用于计算缓冲区大小 (pWidth*pLength+7)/8 B
	Rect_t printRect;
	uint8_t	 *pBuffer;	//page buffer to store print data 
}Page_t;

uint32_t ROTATE4BITTABLE_90[16];
uint32_t ROTATE4BITTABLE_270[16];

void handleError(const char *msg)
{
	fprintf(stderr, "%s\n", msg);
	exit(1);
}

Page_t* NewPage(uint32_t pw, uint32_t pl)
{
	Page_t *page;
	page = (Page_t *)malloc(sizeof(Page_t));
	if(page)
	{
		page->pWidth = (pw+7) & ~0x07;
		page->pLength = pl;
		page->printRect.oriX = page->printRect.oriY = 0;
		page->printRect.endX = pw;
		page->printRect.endY = pl;
		page->pBuffer = (uint8_t*)calloc(page->pWidth_B, page->pLength);
		if(!page->pBuffer) handleError("malloc page buffer fail");
	}
	else
	{
		handleError("malloc page fail");
	}

	return page;
}

Page_t* FreePage(Page_t *page)
{
	if(page)
	{
		if(page->pBuffer) free(page->pBuffer);
		page->pBuffer = NULL;
		free(page);
		page = NULL;
	}
	return page;
}

void PrintPage(const char *fileName, Page_t *page)
{
	FILE *file =fopen(fileName, "wb");
	fwrite(page->pBuffer, page->pWidth_B, page->pLength, file);
	fclose(file);
}

void InitTable(void)
{
	int i, j;
	uint32_t val;
	//翻转码表存在的意义是将1bit存取直接映射成4bit存取，减少bit操作
	//顺时针翻转90°码表,bit存储 LSB--MSB;对于4Byte而言，LSB为点阵的最上面一行
	for(i=0; i<16; ++i)
	{
		val = 0;
		for(j=0; j<4; ++j)
		{
			if(i & 0x01<<j)
			{
				//val |= 0x80000000>>(j<<3);//j*8bit
				val |= 0x00000080<<(j<<3);//j*8bit
			}
		}
		ROTATE4BITTABLE_90[i] = val;
	}
	//顺时针翻转270°码表,bit存储 LSB--MSB;对于4Byte而言，LSB为点阵的最上面一行
	for(i=0; i<16; ++i)
	{
		val = 0;
		for(j=0; j<4; ++j)
		{
			if(i & 0x01<<j)
			{
				val |= 0x01000000>>(j<<3);//j*8bit
			}
		}
		//printf("%01x  %08x\n", i,val);
		ROTATE4BITTABLE_270[i] = val;
	}
}

void RotateNormal_270(Page_t *pdst, Page_t *psrc)
{
	uint8_t MARK, val;
	uint32_t cpw,i;
	uint32_t srow, scol, drow, dcol;
	uint32_t srcdwb, dstdwB, srclwB, srch;
	uint32_t dstlwB;
	uint8_t *dstbuf, *srcbuf;	


	srcdwb = psrc->printRect.endX;	//有效位数
	srclwB = psrc->pWidth_B;	//原页面，一行字节数
	srch = psrc->printRect.endY;		//原页面，高度
	dstlwB = pdst->pWidth_B;	//目标页面，一行字节数
	dstdwB = (srch + 7) >> 3;	//目标数据占的字节数

	assert(pdst && pdst->pBuffer);
	assert(psrc && psrc->pBuffer);
	//目标区域不够大直接报错
	assert(pdst->pLength>=psrc->printRect.endX && pdst->printRect.endX>=psrc->printRect.endY);

	//清空目标区域
	for(drow=0; drow<srcdwb; ++drow)
	{
		memset(pdst->pBuffer + drow*dstlwB, 0, (srch+7)>>3);
	}

	//计算翻转
	for(srow=0; srow<srch; ++srow)
	{
		cpw = 8;
		srcbuf = psrc->pBuffer + srow*srclwB;
		drow = (srcdwb-1);
		dstbuf = pdst->pBuffer + (srow>>3);//源每列对应目标横向数组偏移
		for(scol=0; scol<srcdwb; scol += 8)
		{
			if(cpw+scol > srcdwb) cpw = srcdwb - scol;
			val = srcbuf[scol>>3];//源区域数据
			MARK = 0x01<<(srow&0x07);//目标区域掩码
			if(val)
			{				
				for(i=0; i<cpw; ++i,--drow)
				{
					if(val & (0x01)<<i)
					{
						dstbuf[drow*dstdwB] |= MARK;
					}
				}
			}
			else
			{
				drow -= cpw;
			}
		}
	}
}

void RotateTable_270(Page_t *pdst, Page_t *psrc)
{
	uint8_t MARK, val;
	uint32_t srow, scol, cph, cpmatrixh, cpmatrixw;
	uint32_t srcdwb, dstdwB, srclwB, srch;
	uint32_t dstlwB;
	uint32_t high4ext, low4ext;
	uint8_t *srcbuf, *dstbuf;

	assert(pdst && pdst->pBuffer);
	assert(psrc && psrc->pBuffer);
	//目标区域不够大直接报错
	assert(pdst->pLength>=psrc->printRect.endX && pdst->printRect.endX>=psrc->printRect.endY);

	srcdwb = psrc->printRect.endX;	//有效位数
	srclwB = psrc->pWidth_B;	//原页面，一行字节数
	srch = psrc->printRect.endY;		//原页面，高度
	dstlwB = pdst->pWidth_B;	//目标页面，一行字节数
	dstdwB = (srch + 7) >> 3;	//目标数据占的字节数

	//8*8矩阵翻转
	cpmatrixh = 8;
	cpmatrixw = 8;
	{//顺时针旋转270°
		for(srow=0; srow<srch; srow += 8)
		{//纵向8行
			if(cpmatrixh + srow > srch) cpmatrixh = srch - srow; //高度值非字节对齐
			cpmatrixw = 0;
			for (scol=0; scol<srcdwb; scol += 8)
			{//横向8列
				if(8 + scol > srcdwb) 
				{
					cpmatrixw = srcdwb - scol; //宽度值非字节对齐
				}
				low4ext = high4ext = 0;
				srcbuf = psrc->pBuffer + srow*srclwB + (scol>>3);
				dstbuf = pdst->pBuffer + (srcdwb - scol - 8)*dstlwB + (srow>>3);
				for (cph=0; cph<cpmatrixh; ++cph)
				{
					val = srcbuf[cph*srclwB];
					low4ext |= ROTATE4BITTABLE_270[val&0X0F] << cph;
					high4ext |=ROTATE4BITTABLE_270[(val>>4)&0X0F] << cph;
				}
				//将翻转过的8*8的点阵复制至目标区域，由于有余数，需要所以使用|,否则直接相等
				if(cpmatrixh != 8)
				{
					MARK = 0xFF>>cpmatrixh;
					for(cph=cpmatrixw; cph<8; ++cph)
					{
						if(cph<4)
						{
							dstbuf[cph*dstlwB] = (dstbuf[cph*dstlwB] & MARK) | ((high4ext >> (cph<<3))&0xFF);
						}
						else
						{
							dstbuf[cph*dstlwB] = (dstbuf[cph*dstlwB] & MARK) | ((low4ext >> ((cph-4)<<3))&0xFF);
						}
					}
				}
				else
				{
					for(cph=cpmatrixw; cph<8; ++cph)
					{
						if(cph<4)
						{
							dstbuf[cph*dstlwB] = ((high4ext >> (cph<<3))&0xFF);
						}
						else
						{
							dstbuf[cph*dstlwB] = ((low4ext >> ((cph-4)<<3))&0xFF);
						}
					}
				}
			}
		}
	}
}



int main(int argc, char const *argv[])
{
	uint32_t WIDTH = 640, LENGTH = 640;
	//uint32_t STARTWIDTH = WIDTH-8, STARTLENGTH = LENGTH-8;
	uint32_t STARTWIDTH = 12, STARTLENGTH = 24;
	uint32_t pw, pl, i, j;
	DWORD startN, stopN, startT, stopT;
	Page_t *pdstnormal, *pdsttable, *psrc;
	FILE *filetime = fopen("timefile.txt", "w");

	LARGE_INTEGER nFreq;
	LARGE_INTEGER startCN, stopCN, startCT, stopCT;

	InitTable();
	psrc = NewPage(WIDTH, LENGTH);
	pdstnormal = NewPage(LENGTH, WIDTH);
	pdsttable = NewPage(LENGTH, WIDTH);
	QueryPerformanceFrequency(&nFreq);
	QueryPerformanceCounter(&startCN);
	srand(startCN.LowPart);
	
	for(pw=0; pw<psrc->pWidth_B; ++pw)
	{
		for(pl=0; pl<psrc->pLength; ++pl)
		{
			psrc->pBuffer[pl*psrc->pWidth_B + pw] = rand();
		}
	}

	RotateNormal_270(pdstnormal, psrc);
	RotateTable_270(pdsttable, psrc);

	PrintPage("normalData", pdstnormal);
	PrintPage("tableData", pdsttable);
	
	for(pw=STARTWIDTH; pw<WIDTH; pw+=4)
	{
		psrc->printRect.endX = pw;

		for ( pl = STARTLENGTH; pl < LENGTH; pl += 4)
		{
			psrc->printRect.endY = pl;

			// startN = GetTickCount();
			// RotateNormal_270(pdstnormal, psrc);
			// stopN = GetTickCount();

			// startT = GetTickCount();
			// RotateTable_270(pdsttable, psrc);
			// stopT = GetTickCount();
			// printf("table: %d ms, normal: %d ms\n", stopT - startT, stopN - startN);
			QueryPerformanceCounter(&startCN);
			RotateNormal_270(pdstnormal, psrc);
			QueryPerformanceCounter(&stopCN);

			QueryPerformanceCounter(&startCT);
			RotateTable_270(pdsttable, psrc);
			QueryPerformanceCounter(&stopCT);
			//printf("table: %lld , normal: %lld \n", (stopCT.QuadPart - startCT.QuadPart), (stopCN.QuadPart - startCN.QuadPart));
			fprintf(filetime, "pw: %4d, pl:%4d, table: %8lld, normal: %8lld\n", pw, pl, (stopCT.QuadPart - startCT.QuadPart), (stopCN.QuadPart - startCN.QuadPart));
		}
	}

	// startT = GetTickCount();
	// Sleep(1000);
	// stopT = GetTickCount();
	// printf("time: %d ms\n", stopT - startT);

	return 0;
}
